#ifndef __tcp_hpp__
#define __tcp_hpp__

#include <croco/os/native_handle_type.hpp>
#include <cstddef>

namespace croco
{
namespace ip
{

class connected_tcp;

class tcp
{
public:
    typedef os::native_handle_type native_handle_type;

    struct socket_move
    {
        socket_move(connected_tcp& s);
        native_handle_type handle;
    };

protected:
    friend struct socket_move;

    tcp();
    tcp(native_handle_type handle) : m_handle(handle) {}
    ~tcp();

    native_handle_type native_handle() const { return m_handle; }
    void set_native_handle(native_handle_type handle) { m_handle = handle; }
    void bind(const char* address, int port);
    void connect(const char* address, int port);
    void listen();
    connected_tcp accept();
    int receive(char* buffer, std::size_t size);
    int send(const char* buffer, std::size_t size);
    void close();

private:
    // uncopyable
    tcp(tcp&);
    tcp& operator=(tcp&);

    native_handle_type m_handle;
};

class server_tcp : private tcp
{
public:
    server_tcp(const char* address, int port)
    {
        bind(address, port);
    }

    using tcp::listen;
    using tcp::accept;
    using tcp::native_handle;
};

class connected_tcp : private tcp
{
public:
    friend struct socket_move;

    connected_tcp(const char* address, int port)
    {
        connect(address, port);
    }
    connected_tcp(native_handle_type handle) : tcp(handle) {}
    connected_tcp(socket_move sm) : tcp(sm.handle) {}
    connected_tcp& operator=(socket_move sm);
    operator socket_move();

    using tcp::receive;
    using tcp::send;
    using tcp::native_handle;
    using tcp::close;
};

} // namespace ip
} // namespace croco

#endif // __tcp_hpp__
